package org.tigris.atlas.service.xml.schema;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.xml.stream.XMLStreamReader;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlbeans.XmlException;
import org.tigris.atlas.messages.Message;
import org.tigris.atlas.messages.MessageFactory;
import org.tigris.atlas.messages.Messages;
import org.tigris.atlas.messages.Severity;
import org.tigris.atlas.service.ServiceResponse;
import org.tigris.atlas.service.VoidServiceResponse;
import org.tigris.atlas.service.xml.schema.voidServiceResponse.MessageType;
import org.tigris.atlas.service.xml.schema.voidServiceResponse.MessagesType;
import org.tigris.atlas.service.xml.schema.voidServiceResponse.SeverityType;
import org.tigris.atlas.service.xml.schema.voidServiceResponse.VoidServiceResponseType;
import org.tigris.atlas.service.xml.schema.voidServiceResponse.VoidServiceResponseXMLDocument;
import org.tigris.atlas.transfer.TransferObject;
import org.tigris.atlas.xml.schema.SchemaInstance;

/**
 * This <tt>VoidServiceResponse</tt>-to-Schema translator is used to marshall and unmarshall
 * schema into void service response objects.
 */
public class VoidServiceResponseSchemaAssembler extends SchemaAssembler {

	private static Log log = LogFactory.getLog(VoidServiceResponseSchemaAssembler.class);
	
	/**
	 * Transforms the values contained in the passed schema onto the passed <tt>${entityType}</tt> 
	 * @param schema A schema that contains values to be set onto the passed transfer object
	 * @param to The <tt>${entityType}</tt> instance that will be the recipient of the schema's values
	 */
	//TODO: fix comment
	public VoidServiceResponse createVoidServiceResponse(XMLStreamReader reader) {
		VoidServiceResponse response = new VoidServiceResponse();
		VoidServiceResponseXMLDocument xmlDocument = null;
		
		if (reader != null) {
			try {
				xmlDocument = VoidServiceResponseXMLDocument.Factory.parse(reader);
			} catch (XmlException xmle) {
				log.error("Could not convert schema to java", xmle);
				return null;
			}
			//extract the schema-data for this document:
			VoidServiceResponseType xmlBean = xmlDocument.getVoidServiceResponseXML().getVoidServiceResponse();
			
			if (xmlBean != null) {		
				//pull over all fields:	
				
				//only messages are returned on this instance
								
				//add messages
				if (xmlBean.isSetMessages()) { 						
					MessagesType messagesType = xmlBean.getMessages();
					if (messagesType != null) {
						pullOverMessages(messagesType, response);
					}
				}
			}
				
		}
		
		return response;
		
	}
	
	public VoidServiceResponseXMLDocument createXMLDocument(VoidServiceResponse response) {
		VoidServiceResponseXMLDocument xmlDocument = VoidServiceResponseXMLDocument.Factory.newInstance();
		
		VoidServiceResponseType xmlBean = xmlDocument.addNewVoidServiceResponseXML().
			addNewVoidServiceResponse();
		
		//pull over all fields:
		//only messages exist on this service response
		
		//pull over messages:
		Messages allMessages = response.getMessages();
		int size = allMessages.getErrorMessageCount() + allMessages.getInformationalMessageCount();
		if (size > 0) {
			MessagesType messagesType = xmlBean.addNewMessages();
			createSchema(allMessages, messagesType);
			
		}
		
		return xmlDocument;
	}
	
	//TODO: comment me
	public XMLStreamReader createSchema(VoidServiceResponse response) {
		VoidServiceResponseXMLDocument xmlDocument =  createXMLDocument(response);
		XMLStreamReader reader = xmlDocument.newXMLStreamReader();
		return reader;
	}	
		
	/**
	 * Returns the subclasses <tt>Log</tt> instance to allow for more granular logging
	 * @return <tt>Log</tt> 
	 */
	protected Log getLog() {
		return log;
	}
	
	protected void pullOverMessages(MessagesType messagesType, TransferObject to) {
		if (messagesType != null) {
			Messages messages = to.getMessages();			
			createMessages(messagesType, messages);
		}
	}	
	
	protected void pullOverMessages(MessagesType messagesType, ServiceResponse response) {
		if (messagesType != null) {
			Messages messages = response.getMessages();			
			createMessages(messagesType, messages);
		}
	}
	
	/**
	 * Transforms the values contained in the passed schema onto the passed <tt>Messages</tt> 
	 * @param schema A schema that contains values to be set onto the passed transfer object
	 * @param to The <tt>Messages</tt> instance that will be the recipient of the schema's values
	 */
	public void createMessages(SchemaInstance schema, Messages to) {
		long startTime = 0;
		if (log.isDebugEnabled()) {
			startTime = System.currentTimeMillis();
		}
		
		MessagesType xmlBean = null;
		
		if (schema != null) {
			try {
				xmlBean = MessagesType.Factory.parse(schema.getSchema());
			} catch (XmlException xmle) {
				log.error("Could not convert the following schema to java:" + schema, xmle);
				return;
			}
			//extract the schema-data for this document:
			createMessages(xmlBean, to);						
			
		}
		
		if (log.isDebugEnabled()) {
			long stopTime = System.currentTimeMillis();
			log.debug("Schema to Java transformation for Messages took: " + (stopTime - startTime) + "ms");			
		}			
		
	}
	
	/**
	 * Transforms the values of the passed schema-representation onto a new instance of <tt>Messages</tt>.  This method
	 * signature is needed to allow children to be created, do to the internal implemenation of XMLBeans
	 * @param xmlBean A schema-representation that contains values to be set onto the new transfer object
	 * @return The <tt>Messages</tt> instance that will be the recipient of the schema's values
	 */
	Messages createMessages(MessagesType xmlBean) {
		return createMessages(xmlBean, null);
	}
	
	/**
	 * Performs the actual schema-to-transfer object transformation
	 * @param xmlBean A schema-representation that contains values to be set onto the transfer object
	 * @param to  The <tt>Messages</tt> instance that will be the recipient of the schema's values
	 * @returm  The <tt>Messages</tt> instance that will be the recipient of the schema's values
	 */
	public Messages createMessages(MessagesType xmlBean, Messages messages) {	
		if (messages == null) {
			messages = MessageFactory.createMessages();
		}
		
		if (xmlBean != null) {
			//pull over relations:
			MessageType messageType;
			Message messageTo;
			MessageType[] messageArray = xmlBean.getMessagesArray();
			if ((messageArray != null) && (messageArray.length > 0)) {
				for (int i = 0; i < messageArray.length; i++) {
					messageType = messageArray[i];
					messageTo = createMessage(messageType);
					messages.addMessage(messageTo);
				}
			}
			
		}
		
		return messages;
		
	}
	
	//TODO: comment me
	public SchemaInstance createSchema(Messages messages) {
		long startTime = 0;
		if (log.isDebugEnabled()) {
			startTime = System.currentTimeMillis();
		}
			
		MessagesType xmlBean = MessagesType.Factory.newInstance();
		
		createSchema(messages, xmlBean);
		
		String schema = xmlBean.xmlText();
		
		if (log.isDebugEnabled()) {
			long stopTime = System.currentTimeMillis();
			log.debug("Java to Schema transformation for Messages took: " + (stopTime - startTime) + "ms");			
		}		
		
		//TODO: should probably change to a stream:
		return new SchemaInstance(schema);
	}	
	
	//TODO: comment me
	public MessagesType createSchema(Messages messages, MessagesType xmlBean) {					
		if (messages != null) {
			//pull over relations:
			int numberOfMessages = messages.getErrorMessageCount() + messages.getInformationalMessageCount();
			if (numberOfMessages > 0) {
				Collection messageCollection = new ArrayList(numberOfMessages);
				messageCollection.addAll(messages.getErrorMessages());
				messageCollection.addAll(messages.getInformationalMessages());
				Message child;
				MessageType childXmlBean;
				Iterator setIterator = messageCollection.iterator();
				while (setIterator.hasNext()) {
					child = (Message)setIterator.next();
					childXmlBean = xmlBean.addNewMessages();
					createSchema(child, childXmlBean);					
				}
			}
		}				
				
		return xmlBean;
	}			
	
	/**
	 * Transforms the values contained in the passed schema onto the passed <tt>Message</tt> 
	 * @param schema A schema that contains values to be set onto the passed transfer object
	 * @param to The <tt>Message</tt> instance that will be the recipient of the schema's values
	 */
	public void createMessage(SchemaInstance schema, Message message) {
		long startTime = 0;
		if (log.isDebugEnabled()) {
			startTime = System.currentTimeMillis();
		}
		
		MessageType xmlBean = null;		
		
		if (schema != null) {
			try {
				xmlBean = MessageType.Factory.parse(schema.getSchema());
			} catch (XmlException xmle) {
				log.error("Could not convert the following schema to java:" + schema, xmle);
				return;
			}
			//extract the schema-data for this document:
			createMessage(xmlBean, message);						
			
		}
		
		if (log.isDebugEnabled()) {
			long stopTime = System.currentTimeMillis();
			log.debug("Schema to Java transformation for Message took: " + (stopTime - startTime) + "ms");			
		}			
		
	}
	
	/**
	 * Transforms the values of the passed schema-representation onto a new instance of <tt>Message</tt>.  This method
	 * signature is needed to allow children to be created, do to the internal implemenation of XMLBeans
	 * @param xmlBean A schema-representation that contains values to be set onto the new transfer object
	 * @return The <tt>Message</tt> instance that will be the recipient of the schema's values
	 */
	Message createMessage(MessageType xmlBean) {
		return createMessage(xmlBean, null);
	}
	
	/**
	 * Performs the actual schema-to-transfer object transformation
	 * @param xmlBean A schema-representation that contains values to be set onto the transfer object
	 * @param message  The <tt>Message</tt> instance that will be the recipient of the schema's values
	 * @returm  The <tt>Message</tt> instance that will be the recipient of the schema's values
	 */
	public Message createMessage(MessageType xmlBean, Message message) {	
		if (message == null) {
			message = MessageFactory.createMessage();
		}
		
		if (xmlBean != null) {		
			//pull over all fields:
			if (xmlBean.isSetKey()) {
				message.setKey(transformInboundData(xmlBean.getKey()));
			}
			
			SeverityType severity = xmlBean.getSeverity();
			if (severity != null) {
				Severity enumInstance = Severity.getSeverity(severity.getEnumerationValue());
				message.setSeverity(enumInstance);
			}
			
			String[] insertArray = xmlBean.getInsertArray();
			if (insertArray != null) {
				int length = insertArray.length;
				for (int i = 0; i < length; i++) {
					message.addInsert(insertArray[i]);
				}
			}
			
			String[] propertyArray = xmlBean.getPropertyArray();
			if (propertyArray != null) {
				int length = propertyArray.length;
				for (int i = 0; i < length; i++) {
					message.addProperty(propertyArray[i]);
				}
			}
			
		}
		
		return message;
		
	}
	
	//TODO: comment me
	public SchemaInstance createSchema(Message message) {
		long startTime = 0;
		if (log.isDebugEnabled()) {
			startTime = System.currentTimeMillis();
		}
			
		MessageType xmlBean = MessageType.Factory.newInstance();
		
		createSchema(message, xmlBean);
		
		String schema = xmlBean.xmlText();
		
		if (log.isDebugEnabled()) {
			long stopTime = System.currentTimeMillis();
			log.debug("Java to Schema transformation for Message took: " + (stopTime - startTime) + "ms");			
		}		
		
		return new SchemaInstance(schema);
	}	
	
	//TODO: comment me
	public MessageType createSchema(Message message, MessageType xmlBean) {					
		if (message != null) {
			//pull over all fields:			
			String key = message.getKey();
			if (key != null) {	
				xmlBean.setKey(transformOutboundData(key));
			}
			
			Severity severity = message.getSeverity();
			if (severity != null) {
				SeverityType severityType = xmlBean.addNewSeverity();
				severityType.setEnumerationValue(severity.getName());
			}
			
			Collection insertCollection = message.getInserts();
			if ((insertCollection != null) && (insertCollection.size() > 0)) {
				String insert;
				Iterator i = insertCollection.iterator();
				while (i.hasNext()) {
					insert = (String)i.next();
					xmlBean.addInsert(insert);
					
				}
			}
			
			Collection propertyCollection = message.getProperties();
			if ((propertyCollection != null) && (propertyCollection.size() > 0)) {
				String property;
				Iterator i = propertyCollection.iterator();
				while (i.hasNext()) {
					property = (String)i.next();
					xmlBean.addProperty(property);
					
				}
			}
			
		}				
				
		return xmlBean;
	}	
	
}
